#!/usr/bin/env python
#

import sys, time, struct

from spinage.help import logging

from spinage.accessor import Accessor
from spinage.response import RC_OK

from spinage.help.protocol.simpletextsize import SimpleTextSizeProtocolFactory


logger = None

address_list = (
        ('221.194.137.35',4761),('221.194.137.36',4761),('221.194.137.37',4761),('221.194.137.38',4761),
        ('221.194.137.39',4761),('221.194.137.40',4761),('221.194.137.41',4761),('221.194.137.42',4761),
        ('221.194.137.43',4761),('221.194.137.44',4761),('221.194.137.45',4761),('221.194.137.46',4761),
        ('221.194.137.47',4761),('221.194.137.48',4761),('221.194.137.49',4761),('221.194.137.50',4761),
        ('221.194.137.51',4761),('221.194.137.52',4761),('221.194.137.53',4761),('221.194.137.54',4761),
    )   


def md5bin( md5_hex ) :
    m = [ int(md5_hex[i:i+2],16) for i in range(0,32,2)]
    return ''.join( [ struct.pack('B', x) for x in m ] )
    ## md5bin()

def hashToHost( key ) :
    k = md5bin( key )

    n = struct.unpack('<I', k[:4])[0]
    #print '%s %X' % (key,n)
    return n % len(address_list)
    ## hashToHost()

def hashToHost_nomd5( key ) :
    m = [ int(key[i:i+2],16) for i in range(0,8,2)]

    n = m[0] + (m[1] << 8) + (m[2] << 16) + (m[3] << 24)
    #print '%s %X' % (key,n)
    return n % len(address_list)
    ## hashToHost_nomd5()

def hashToHost_int( key ) :
    m = [ key[i:i+2] for i in range(0,8,2)]
    
    n = int( ''.join((m[3],m[2],m[1],m[0])), 16 )
    return n % len(address_list)
    ## hashToHost_int()

hashToHost_func = hashToHost_int

def testPerf_hashToHost() :
    md5_hex = '46b34143cdef308867dbd0f83f4587b6'
    
    TIMES = 1000000
    time_start = time.time()
    for i in xrange(TIMES) :
        x = hashToHost( md5_hex )
    time_last = time.time() - time_start
    tps = TIMES / time_last

    logger.info(' test hashToHost %d times last %.2f seconds, tps:%.2f', TIMES, time_last, tps)
    
    TIMES = 1000000
    time_start = time.time()
    for i in xrange(TIMES) :
        x = hashToHost_nomd5( md5_hex )
    time_last = time.time() - time_start
    tps = TIMES / time_last

    logger.info(' test hashToHost_nomd5 %d times last %.2f seconds, tps:%.2f', TIMES, time_last, tps)
    
    TIMES = 1000000
    time_start = time.time()
    for i in xrange(TIMES) :
        x = hashToHost_int( md5_hex )
    time_last = time.time() - time_start
    tps = TIMES / time_last

    logger.info(' test hashToHost_int %d times last %.2f seconds, tps:%.2f', TIMES, time_last, tps)
    ## test_md5bin()


def testPerf_md5bin() :
    md5_hex = '46b34143cdef308867dbd0f83f4587b6'
    
    TIMES = 1000000
    time_start = time.time()
    for i in xrange(TIMES) :
        x = md5bin( md5_hex )
        n = struct.unpack('<I', x[:4])[0]
    time_last = time.time() - time_start
    tps = TIMES / time_last

    logger.info(' test md5bin %d times last %.2f seconds, tps:%.2f', TIMES, time_last, tps)
    ## test_md5bin()


def test_hashToHost() :
    items = {
            6 : ('46b34143cdef308867dbd0f83f4587b6',
                 '7a14b0e9ed9ae89b38c41172fbe3f267',
                 'bebfb040954246d02b67016c16090e03',
                 'c24bb4a2d50ee6337a908fb2fefbc77e',
                 '5e39ccf6451fe5ea659e0a1bc026ec64',
                 '169c53af8d8039cafa05b2286ccfda4e',
                 '523a8daa1dc517a9feeb9eff0a9d15a1',
                 '6a285613f532e7ca988224809ad3e714',
                 'fa183abda505d0875f84cfc26ec4a69c',
                 '468628f795f5231dfd887f27a7a62d8b',
                ),
            16: ('0c1e507c157ef9558469f7c1322ef179',
                 '64ab6536b5c3f18b970af23acc225068',
                 'fc0f54f0cd6fe74f4f5efdd12de5f491',
                 'c82b09e065e0986309a1cabb7de6280c',
                 'c4623f4a95e0486595746255cd758be8',
                 '68abbe38eddac88d39632d93c005a84e',
                 'b00772c2c54655a49f0ddcdd1d453ac0',
                 '58f420ca65a9226b50ac9e246bd543c3',
                 '34cba76de5fa94d1e3f55d15e39b806d',
                 '00621197d5bc472c4c786d21520e86ce',
                ),
            0 : ('a03913590134720fd9b7261c1a15ef35',
                 '085300dba1fcbe22d979ffd270e91daa',
                 'ecd1bd568947a5137bcb2bdf4cb7ed26',
                 '14086862e9a20e67ef00c9a99144d01b',
                 '0c9d722a89a611cf774629d447a808ba',
                 'b8c7558941af3db56f10b485061188ed',
                 '50e7c5fc012420444fbf502e71fdf750',
                 '9c379942b1a36ccc6b2c395e7cc011f8',
                 '30121bcf69b802a1b817ea604e30116f',
                 '54d0cef22995bc5ef3b9992c0d26975f',
                ),
        }

    for host_want,keys in items.iteritems() :
        for k in keys :
            host_id = hashToHost_func( k )
            if host_id != host_want :
                logger.error('bad hash for key %r, GOT %2d, WANT %2d',  k, host_id, host_want)
                raise RuntimeError
    ## test_hashToHost()


def test_call01( accessor ) :
    #for key in ('46b34143cdef308867dbd0f83f4587b6', '7a14b0e9ed9ae89b38c41172fbe3f267', 'bebfb040954246d02b67016c16090e03', 'c24bb4a2d50ee6337a908fb2fefbc77e', '5e39ccf6451fe5ea659e0a1bc026ec64', '169c53af8d8039cafa05b2286ccfda4e', '523a8daa1dc517a9feeb9eff0a9d15a1', '6a285613f532e7ca988224809ad3e714', 'fa183abda505d0875f84cfc26ec4a69c', '468628f795f5231dfd887f27a7a62d8b' ) :
    #for key in ('0c1e507c157ef9558469f7c1322ef179', '64ab6536b5c3f18b970af23acc225068', 'fc0f54f0cd6fe74f4f5efdd12de5f491', 'c82b09e065e0986309a1cabb7de6280c', 'c4623f4a95e0486595746255cd758be8', '68abbe38eddac88d39632d93c005a84e', 'b00772c2c54655a49f0ddcdd1d453ac0', '58f420ca65a9226b50ac9e246bd543c3', '34cba76de5fa94d1e3f55d15e39b806d', '00621197d5bc472c4c786d21520e86ce' ) :
    for key in ('a03913590134720fd9b7261c1a15ef35', '085300dba1fcbe22d979ffd270e91daa', 'ecd1bd568947a5137bcb2bdf4cb7ed26', '14086862e9a20e67ef00c9a99144d01b', '0c9d722a89a611cf774629d447a808ba', 'b8c7558941af3db56f10b485061188ed', '50e7c5fc012420444fbf502e71fdf750', '9c379942b1a36ccc6b2c395e7cc011f8', '30121bcf69b802a1b817ea604e30116f', '54d0cef22995bc5ef3b9992c0d26975f' ) :
        request = '<request><action>query</action><md5>%s</md5></request>' % key
        idx = hashToHost_func( key )
        address = address_list[ idx ]

        logger.info('send request %s ...', request)
        rc,response = accessor.call(request, address)
        logger.info('receive response (RC:%s) %s', rc, response)

    ## test_call01()

def test_call02( accessor ) :
    count = 0
    count_failure = 0
    sum_bytes = 0

    time_start = time.time()
    for i in xrange(1000) :
        #for key in ('46b34143cdef308867dbd0f83f4587b6', '7a14b0e9ed9ae89b38c41172fbe3f267', 'bebfb040954246d02b67016c16090e03', 'c24bb4a2d50ee6337a908fb2fefbc77e', '5e39ccf6451fe5ea659e0a1bc026ec64', '169c53af8d8039cafa05b2286ccfda4e', '523a8daa1dc517a9feeb9eff0a9d15a1', '6a285613f532e7ca988224809ad3e714', 'fa183abda505d0875f84cfc26ec4a69c', '468628f795f5231dfd887f27a7a62d8b' ) :
        #for key in ('0c1e507c157ef9558469f7c1322ef179', '64ab6536b5c3f18b970af23acc225068', 'fc0f54f0cd6fe74f4f5efdd12de5f491', 'c82b09e065e0986309a1cabb7de6280c', 'c4623f4a95e0486595746255cd758be8', '68abbe38eddac88d39632d93c005a84e', 'b00772c2c54655a49f0ddcdd1d453ac0', '58f420ca65a9226b50ac9e246bd543c3', '34cba76de5fa94d1e3f55d15e39b806d', '00621197d5bc472c4c786d21520e86ce' ) :
        for key in ('a03913590134720fd9b7261c1a15ef35', '085300dba1fcbe22d979ffd270e91daa', 'ecd1bd568947a5137bcb2bdf4cb7ed26', '14086862e9a20e67ef00c9a99144d01b', '0c9d722a89a611cf774629d447a808ba', 'b8c7558941af3db56f10b485061188ed', '50e7c5fc012420444fbf502e71fdf750', '9c379942b1a36ccc6b2c395e7cc011f8', '30121bcf69b802a1b817ea604e30116f', '54d0cef22995bc5ef3b9992c0d26975f' ) :
            request = '<request><action>query</action><md5>%s</md5></request>' % key
            idx = hashToHost_func( key )
            address = address_list[ idx ]

            #logger.info('send request %s ...', request)
            rc,response = accessor.call(request, address, 1000)
            #logger.info('receive response (RC:%s) %s', rc, response)
            if rc != RC_OK :
                count_failure += 1
            else :
                sum_bytes += len( response )

            count += 1

    time_last = time.time() - time_start
    tps = count / time_last
    bw = sum_bytes / time_last / 1048576.0

    logger.info('  test call additional %d times (fails %d) last %.2f seconds, tps:%.2f, BW:%.2f MB/s', count, count_failure, time_last, tps, bw)
    ## test_call02()

def runTest( accessor ) :
    #testPerf_md5bin()
    #testPerf_hashToHost()

    test_hashToHost()

    test_call01( accessor )
    test_call02( accessor )

    ## runTest()


if  __name__ == '__main__' :

    logger = logging.getLogger()

    protocol_factory = SimpleTextSizeProtocolFactory()

    c = Accessor()

    c.setProtocolFactory( protocol_factory )

    runTest( c )

    sys.exit(0)

